package cn.kerui.manage.aspect;

import cn.hutool.core.lang.Assert;
import cn.kerui.common.exception.ServiceException;
import cn.kerui.common.framework.util.lang.CollectionUtil;
import cn.kerui.common.framework.util.lang.ObjectUtil;
import cn.kerui.common.framework.util.lang.StringUtil;
import cn.kerui.common.helper.RequestHelper;
import cn.kerui.common.redis.constants.AuthRedisConstants;
import cn.kerui.common.redis.helper.RedisHelper;
import cn.kerui.common.satoken.annotation.PermissionAuthAdvice;
import cn.kerui.repos.entities.sys.SysMenu;
import cn.kerui.repos.mapper.manageuser.ManageUserInfoMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>创建于 2024/4/24 14:53 </p>
 *
 * @author yangkai
 * @version v1.0
 * @since
 */
@Aspect
@Component
@Slf4j
@Order(1)
public class PermissionAuthRequestAspect {

    @Autowired
    private ManageUserInfoMapper manageUserInfoMapper;

    @Autowired
    private RedisHelper redisHelper;

    @Pointcut(value = "execution(* cn.kerui.*.controller.*.*Controller..*(..))")
    public void point() {

    }

    /**
     * 判断接口是否需要权限，如果需要权限，则判断用户是否拥有权限
     * @param jp
     * @return
     * @throws Throwable
     */
    @Around(value = "point()")
    public Object around(ProceedingJoinPoint jp) throws Throwable {
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = signature.getMethod();

        log.info("获取请求方法的类的class文件, method = {}", method.getName());

        Class<?> declaringClass = method.getDeclaringClass();

        if (isPermissionAuth(method, declaringClass) && !RequestHelper.isSuperAdmin()) {
            // 需要校验用户权限
            PermissionAuthAdvice permissionAuthAdvice = looseFindPermissionAuthAdvice(method);
            Optional.ofNullable(permissionAuthAdvice.value())
                    .ifPresent(value -> {
                        log.info("获取权限校验注解, value = {}", value);
                        int count = StringUtil.count(value, "::");
                        Assert.isTrue(count >= 2, () -> new ServiceException("权限校验配置有误, 请检查配置"));
                        Set<String> perms = getUserPermission(RequestHelper.getUserId());
                        boolean authFlag = CollectionUtil.anyMatch(perms, t -> t.equals(value));
                        Assert.isTrue(authFlag, () -> new ServiceException("未拥有操作权限，请联系管理员分配!!!"));
                    });
        }
        return jp.proceed();
    }

    /**
     * 判断是否存在{@link PermissionAuthAdvice}注解
     * @param method
     * @param clazz
     * @return
     */
    private boolean isPermissionAuth(Method method, Class<?> clazz) {
        return method.getAnnotation(PermissionAuthAdvice.class) != null || clazz.getAnnotation(PermissionAuthAdvice.class) != null;
    }

    /**
     * 宽松查找PermissionAuthAdvice注解
     */
    private PermissionAuthAdvice looseFindPermissionAuthAdvice(Method method) {
        return ObjectUtil.lazyCoalesce(ObjectUtil::isNotBlank, () -> method.getAnnotation(PermissionAuthAdvice.class), () -> method.getDeclaringClass().getAnnotation(PermissionAuthAdvice.class));
    }

    /**
     * 获取用户权限信息
     * @param userId
     * @return
     */

    public Set<String> getUserPermission(Long userId) {
        if (redisHelper.existKey(AuthRedisConstants.USER_PERMS_KEY, String.valueOf(userId))) {
            return redisHelper.getObj(AuthRedisConstants.USER_PERMS_KEY, String.valueOf(userId), Set.class);
        }
        List<SysMenu> userPerm = manageUserInfoMapper.getUserPerm(userId);
        if (CollectionUtil.isEmpty(userPerm)) {
            return null;
        }
        Set<String> perms = userPerm.stream().map(SysMenu::getPerms).collect(Collectors.toSet());
        redisHelper.setObj(AuthRedisConstants.USER_PERMS_KEY, String.valueOf(userId), perms, 24, TimeUnit.HOURS);
        return perms;
    }
}
